// SPDX-License-Identifier: GPL-2.0
#ifndef _NF_CT_DEFRAG_H
#define _NF_CT_DEFRAG_H

#include <linux/skbuff.h>

#ifdef CONFIG_ONT_NET_FEATURES

#include <linux/err.h>
#include <linux/if_arp.h>
#include <net/ip.h>
#include <net/ip6_route.h>

static inline bool is_dslite_tnl(const char *dev_name)
{
	return (dev_name[0] == 'd' && dev_name[1] == 's' && dev_name[2] == 'l' &&
		dev_name[3] == 'i' && dev_name[4] == 't' && dev_name[5] == 'e');
}

static inline bool ipv4_is_from_tnl6(const struct sk_buff *skb, bool ctrl)
{
	if (!skb->dev)
		return false;

	if (ctrl && !is_dslite_tnl(skb->dev->name))
		return false;

	return (skb->dev->type == ARPHRD_TUNNEL6);
}

static inline bool ipv4_is_to_tnl6(const struct sk_buff *skb, bool ctrl)
{
	struct rtable *rt;
	struct flowi4 fl4;
	bool ret = false;

	if (!skb->dev)
		return false;

	memset(&fl4, 0, sizeof(fl4));
	fl4.daddr = ip_hdr(skb)->daddr;
	rt = ip_route_output_key(dev_net(skb->dev), &fl4);
	if (!IS_ERR(rt)) {
		if ((rt->dst.dev->type == ARPHRD_TUNNEL6) &&
		    (!ctrl || is_dslite_tnl(rt->dst.dev->name)))
			ret = true;
		ip_rt_put(rt);
	}

	return ret;
}

/* No gather frags and nfct if packets are to pass through 4in6 dslite tunnel.
 * Set ctrl to true if need to check whether it is a DS-lite tunnel. (MAP-E ?)
 */
static inline bool ipv4_is_through_tnl6(const struct sk_buff *skb, bool ctrl)
{
	return (ipv4_is_from_tnl6(skb, ctrl) || ipv4_is_to_tnl6(skb, ctrl));
}

/* for downstream packet */
static inline bool ipv6_is_from_6rd(const struct sk_buff *skb)
{
	return (skb->dev && skb->dev->type == ARPHRD_SIT);
}

/* for upstream packet */
static inline bool ipv6_is_to_6rd(const struct sk_buff *skb)
{
	struct rt6_info *rt;
	bool ret = false;

	rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb)->saddr, NULL, 0, NULL, 0);
	if (rt && rt->dst.dev) {
		if (rt->dst.dev->type == ARPHRD_SIT)
			ret = true;
		dst_release(&rt->dst);
	}

	return ret;
}

/* No gather frags and nfct if packets are to pass through 6rd tunnel. */
static inline bool ipv6_is_through_6rd(const struct sk_buff *skb)
{
	return (ipv6_is_from_6rd(skb) || ipv6_is_to_6rd(skb));
}

#else /* CONFIG_ONT_NET_FEATURES */

static inline bool ipv4_is_through_tnl6(const struct sk_buff *skb, bool ctrl)
{
	return false;
}

static inline bool ipv6_is_through_6rd(const struct sk_buff *skb)
{
	return false;
}

#endif /* CONFIG_ONT_NET_FEATURES */

#endif /* _NF_CT_DEFRAG_H */
